Skip to content

DEBUG refinements#652

Merged
dustinswales merged 9 commits into
NCAR:developfrom
dustinswales:feature/debug_refinements
May 14, 2025
Merged

DEBUG refinements#652
dustinswales merged 9 commits into
NCAR:developfrom
dustinswales:feature/debug_refinements

Conversation

@dustinswales
Copy link
Copy Markdown
Member

@dustinswales dustinswales commented Mar 17, 2025

This PR contains changes for the DEBUG tests.

Add functionality within the DEBUG checks to:

  • Allow case-insensitivity for standard_names within dimensions.
  • Permit numerical dimensions in metadata.
  • Skip certain debug checks for arrays with non-standard lower-bounds
  • Remove tests for character and type arrays (not working)
  • Capgen tests updated to test new capabilities.

User interface changes?:
No

Fixes: #649 #650 #651 #652

Copy link
Copy Markdown
Collaborator

@climbfuji climbfuji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, just a few minor corrections. Thanks for your efforts!

Comment thread scripts/suite_objects.py Outdated
Comment thread test/capgen_test/test_host.F90 Outdated
Co-authored-by: Dom Heinzeller <dom.heinzeller@icloud.com>
@climbfuji
Copy link
Copy Markdown
Collaborator

@dustinswales I am happy to approve this PR now, but I am wondering if it's still needed given #651?

@dustinswales
Copy link
Copy Markdown
Member Author

@DomHeinzeller After discussion in #651, the preferable solution is to redo this one debug check to check the length of each dimension, and not use the bounds of the array.
Then I could remove this new attribute.

This will take some work, and I will not be able to get to this for a few days.

@climbfuji
Copy link
Copy Markdown
Collaborator

Ok, thanks. Please ping me when the revised version of this PR is ready.

@climbfuji climbfuji marked this pull request as draft March 31, 2025 16:41
@gold2718
Copy link
Copy Markdown
Collaborator

gold2718 commented Apr 5, 2025

When these changes are done, I think an adjustment of the Input/Output Variable (argument) Rules is in order.

One idea is to modify the warning and example to:

.. warning:: Fortran assumes that the lower bound of assumed-size arrays is ``1``. If a scheme uses ``foo`` with lower bounds ``is`` and ``ks`` that are different from ``1``, then these must be specified explicitly:

  .. code-block:: fortran

     real(kind=kind_phys), dimension(is:,ks:), intent(inout) :: foo

@dustinswales dustinswales marked this pull request as ready for review April 22, 2025 18:17
@dustinswales
Copy link
Copy Markdown
Member Author

@gold2718 I've made the discussed modifications. This is ready for review @climbfuji @peverwhee

Comment thread test/capgen_test/temp_set.F90
Copy link
Copy Markdown
Collaborator

@gold2718 gold2718 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks okay now but I did not see any check for failure. Is there a test showing proper output is triggered when an array fails the check?
I tried changing one of the dimensions of var_array in capgen_test/test_host_mod and the test still passes. Is that expected? capgen was called with the --debug flag.
Is this a valid test?

@dustinswales
Copy link
Copy Markdown
Member Author

@gold2718 I can't figure out how to trigger these errors!?!

I haven't really thought hard about this, but do we need these size checks at all?
If host and scheme files have differing dimensions, it will get caught during parsing, so isn't this check redundant? (i.e. there isn't anything going on in the Cap that wasn't described in the metadata files)
@climbfuji @peverwhee

@climbfuji
Copy link
Copy Markdown
Collaborator

@gold2718 I can't figure out how to trigger these errors!?!

I haven't really thought hard about this, but do we need these size checks at all? If host and scheme files have differing dimensions, it will get caught during parsing, so isn't this check redundant? (i.e. there isn't anything going on in the Cap that wasn't described in the metadata files) @climbfuji @peverwhee

The purpose of this is not to check if the metadata is consistent between host and scheme. It's to check if the allocation of the variable by the host is different than what the metadata says.

@gold2718
Copy link
Copy Markdown
Collaborator

@gold2718 I can't figure out how to trigger these errors!?!

I haven't really thought hard about this, but do we need these size checks at all? If host and scheme files have differing dimensions, it will get caught during parsing, so isn't this check redundant? (i.e. there isn't anything going on in the Cap that wasn't described in the metadata files) @climbfuji @peverwhee

capgen is thwarting us!

See how var_array is being passed into the suite cap from test_host_ccpp_cap.F90:

var_array=var_array(col_start:col_end, 1:2, 1:4, 1:6))

@mkavulich
Copy link
Copy Markdown
Collaborator

@gold2718 @DomHeinzeller @dustinswales I have opened a PR for changing the tech doc with the suggested wording: NCAR/ccpp-doc#75

@gold2718
Copy link
Copy Markdown
Collaborator

I am not so sure about the intents, though. But that might be because I am still confused about the intent. An intent(out) doesn't mean the variable must be allocated by the scheme, or? It could just mean that the scheme updates the information (completely) for an already allocated variable.

This is not correct. Upon subroutine entry, an intent(out) allocatable array becomes "not currently allocated" (and thus, is deallocated if necessary). This is a necessary feature of the language because it is not allowed to allocate an already allocated (allocatable) array (try it).

@climbfuji
Copy link
Copy Markdown
Collaborator

climbfuji commented Apr 29, 2025

@gold2718 I am referring to this situation of which there are probably more than 100 in the current ccpp-physics: (bar takes the place of the scheme _run, _timestep_init, etc subroutine):

program alloctest

  implicit none

  real, allocatable :: foo(:)

  allocate(foo(1:7))
  foo = 0.0
  call bar(foo)
  
  print *, shape(foo)
  print *, foo

contains

  subroutine bar(foo)
    real, intent(out) :: foo(:)
    foo = 7.0
  end subroutine bar

end program alloctest

with result

[dom@bounty alloctest]$ gfortran -o main.x main.F90
[dom@bounty alloctest]$ ./main.x
           7
   7.00000000       7.00000000       7.00000000       7.00000000       7.00000000       7.00000000       7.00000000

This is how the intent is used in the ccpp-physics and ccpp-framework (prebuild) as of today. If something is intent(out), then it means the data must be overwritten entirely, it does not imply or even allow a reallocation of the array. Remember that in the good old days, the rule was that the host model is responsible for ALL memory management.

@dustinswales
Copy link
Copy Markdown
Member Author

This is how the intent is used in the ccpp-physics and ccpp-framework (prebuild) as of today. If something is intent(out), then it means the data must be overwritten entirely, it does not imply or even allow a reallocation of the array. Remember that in the good old days, the rule was that the host model is responsible for ALL memory management.

@climbfuji This was my understanding.
Capgen adds a wrinkle to this by "offloading the memory mgmt" by creating suite cap variables that aren't defined/allocated by the host. However, that doesn't mean the host won't still manage all the memory themselves, which is the status of the UFS physics.

@gold2718
Copy link
Copy Markdown
Collaborator

This is how the intent is used in the ccpp-physics and ccpp-framework (prebuild) as of today.

Thanks for the example, I now see we are talking about different things. You are talking about a regular, intent(out) array while I was talking about "internally-generated scheme allocatable arrays". The key difference is that for my examples, bar would look like:

  subroutine bar(foo)
    real, allocatable, intent(out) :: foo(:)
    foo = 7.0
  end subroutine bar

which would not work!

If something is intent(out), then it means the data must be overwritten entirely, it does not imply or even allow a reallocation of the array.

How would you test that? There is nothing in Fortran (aside from possible compiler warnings) that enforces that. In your example, if you change the assignment to foo(3) = 7.0, then even --warn-all on gfortran will not complain.

@climbfuji
Copy link
Copy Markdown
Collaborator

This is not about testing whether the data must be overwritten entirely. This is about having to test the allocation of the array if allocated by the host model, regardless of whether the intent is in, inout, or out (in reply to comment #652 (comment)).

And also, you can catch the missing assignment of data to an intent(out) array quite easily with generated code. For example, when debug checks are enabled, every array that is declared as intent(out) can be assigned a value that would either cause a runtime error if being used in the scheme, or that can be tested for after the scheme was called.

@gold2718
Copy link
Copy Markdown
Collaborator

This is about having to test the allocation of the array if allocated by the host model, regardless of whether the intent is in, inout, or out (in reply to comment #652 (comment)).

I guess that is why I am confused. I did call for testing all "host model" allocatable arrays or pointers in the host cap. The only other arrays which need to be tested are intent(out) scheme allocatable arrays and these would be tested in the suite cap. It sounds like the these scheme tests would never be generated for UFS schemes but other schemes could have such arrays. I do not think we have a disagreement here.

@gold2718
Copy link
Copy Markdown
Collaborator

And also, you can catch the missing assignment of data to an intent(out) array quite easily with generated code. For example, when debug checks are enabled, every array that is declared as intent(out) can be assigned a value that would either cause a runtime error if being used in the scheme, or that can be tested for after the scheme was called.

This is only the case if you reserve one or more values to the CCPP Framework. Otherwise, a scheme could use any value (including huge(1.0_kind_phys)) for some internal purpose. If you did make such a reservation, you could set it on the way in and test that no such values are in the array on the way out.

One candidate that might be acceptable is NaN. Some compilers have a flag to initialize all data to NaN so that tests can be run to catch the use of uninitialized data.

@climbfuji
Copy link
Copy Markdown
Collaborator

And also, you can catch the missing assignment of data to an intent(out) array quite easily with generated code. For example, when debug checks are enabled, every array that is declared as intent(out) can be assigned a value that would either cause a runtime error if being used in the scheme, or that can be tested for after the scheme was called.

This is only the case if you reserve one or more values to the CCPP Framework. Otherwise, a scheme could use any value (including huge(1.0_kind_phys)) for some internal purpose. If you did make such a reservation, you could set it on the way in and test that no such values are in the array on the way out.

One candidate that might be acceptable is NaN. Some compilers have a flag to initialize all data to NaN so that tests can be run to catch the use of uninitialized data.

I was thinking the same, yes!

@climbfuji
Copy link
Copy Markdown
Collaborator

With #658, I think we should go ahead and merge this PR. Can you take another look, please, @gold2718?

@climbfuji climbfuji requested a review from gold2718 May 1, 2025 16:42
Copy link
Copy Markdown
Collaborator

@gold2718 gold2718 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically okay (for now) but could use some cleanup.

  • The PR description is now out of date
  • Please clean debug stings out of comments.

Comment thread scripts/suite_objects.py Outdated
Comment thread scripts/suite_objects.py Outdated
Comment thread scripts/suite_objects.py Outdated
@climbfuji climbfuji linked an issue May 12, 2025 that may be closed by this pull request
@dustinswales
Copy link
Copy Markdown
Member Author

Also, is there a reason you are testing size(var_array(1,1,:,1)) instead of size(var_array, 3)?

@gold2718 Not that I recall.

@dustinswales
Copy link
Copy Markdown
Member Author

@gold2718 @climbfuji @peverwhee
I made two changes that need re-review before merging.

  1. Fix error messaging issue in Debug checks output unreadable writes #590 (see autogenerated code snippet below).
  2. Skip "length check" for 1D arrays, since same as "size check" in this case (see "ps" below)

Screenshot 2025-05-13 at 9 37 06 AM

@climbfuji
Copy link
Copy Markdown
Collaborator

@gold2718 @climbfuji @peverwhee I made two changes that need re-review before merging.

  1. Fix error messaging issue in Debug checks output unreadable writes #590 (see autogenerated code snippet below).
  2. Skip "length check" for 1D arrays, since same as "size check" in this case (see "ps" below)

Screenshot 2025-05-13 at 9 37 06 AM

Thanks @dustinswales, this all looks good to me. Happy to proceed with merging the PR as it is now.

@peverwhee
Copy link
Copy Markdown
Collaborator

@dustinswales looks good to me!

@dustinswales dustinswales merged commit 7b26420 into NCAR:develop May 14, 2025
19 checks passed
mkavulich added a commit to NCAR/ccpp-doc that referenced this pull request Jun 5, 2025
@dustinswales dustinswales deleted the feature/debug_refinements branch September 18, 2025 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants